﻿#region << 文件说明 >>

/*----------------------------------------------------------------
 * 命名空间：Vampirewal.Core.WebApi.Middlewares
 * 唯一标识：e3ef6706-8603-44c0-8ffe-b566a1685f57
 * 文件名：VampirewalRoutingMiddleware
 *
 * 创建者：杨程
 * 电子邮箱：235160615@qq.com
 * 创建时间：2023/10/7 17:59:15
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/

#endregion << 文件说明 >>

namespace Vampirewal.Core.WebApi;

/// <summary>
/// Vampirewal路由中间件，一定要最后加载！
/// </summary>
internal sealed class VampirewalRoutingMiddleware : IVampirewalMiddleware
{
    //private readonly HttpListenerContext _context;
    private readonly IServiceProvider _serviceProvider;

    private readonly IVampirewalApiResultProvider _ApiResult;

    public VampirewalRoutingMiddleware(IServiceProvider serviceProvider, IVampirewalApiResultProvider ApiResult)
    {
        //_context = context;
        _serviceProvider = serviceProvider;
        _ApiResult = ApiResult;
    }

    public async Task InvokeAsync(HttpListenerContext context, Func<Task> next)
    {
        string ErrorMsg = string.Empty;

        (int Code, string Msg, Exception Ex) Mate = new() { Code = 200, Msg = "" };

        if (context.Request.RawUrl.EndsWith(".html"))
        {
            //说明请求的是静态页面
            if (context.Request.RawUrl.Equals("/apidoc/index.html"))
            {
                var config = _serviceProvider.GetRequiredService<VampirewalCoreWebApiConfig>();

                if (config.IsGeneratorWebApiDoc)
                {
                    await context.Response.OutputStream.WriteAsync(Encoding.UTF8.GetBytes(config.ApiDocStr));

                    return;
#if DEBUG

#elif RELEASE

                //using (var fs=new FileStream($"{config.Option.LocalPath}//ApiDoc.html", FileMode.Open, FileAccess.Read, FileShare.Read))
                //{
                //    byte[] infbytes = new byte[(int)fs.Length];
                //    await fs.ReadAsync(infbytes, 0, infbytes.Length);

                //    string apidoc=Encoding.UTF8.GetString(infbytes);

                //    await context.Response.OutputStream.WriteAsync(Encoding.UTF8.GetBytes(apidoc));
                //}
#endif
                }
                else
                {
                    Mate.Code = 404;
                    Mate.Msg = "未启用UseWebApiDoc！";
                    goto End;
                    //context.Response.StatusCode = 404;
                    //var apiresult = await _ApiResult.OnResponseStatusCodes(404, errorInfo: "未启用UseWebApiDoc！");
                    //await context.Response.OutputStream.WriteAsync(Encoding.UTF8.GetBytes(apiresult));
                }
            }
        }

        //HttpMethodEnum method = HttpMethodEnum.POST;
        string RoutePath = string.Empty;

        Dictionary<string, string> GetParams = new Dictionary<string, string>();

        try
        {
            if (context.Request.HttpMethod == "GET")
            {
                //说明是GET
                //method = HttpMethodEnum.GET;

                var RawUrls = context.Request.RawUrl.Substring(1).Split("?");
                RoutePath = RawUrls[0];

                var UrlParams = RawUrls[1].Split("&");
                foreach (var item in UrlParams)
                {
                    var request = item.Split("=");
                    GetParams.Add(request[0].ToLower(), request[1]);
                }

                if (VampirewalCoreWebApiExtension.Routes.TryGetValue(RoutePath, out var Action))
                {
                    var Params = Action.Method.GetParameters();

                    List<object> PassParams = new List<object>();

                    foreach (var param in Params)
                    {
                        GetParams.TryGetValue(param.Name.ToLower(), out var value);

                        var converValue = StringToOtherConver(param.ParameterType, value);

                        PassParams.Add(converValue);
                    }

                    await ExcuteAction(Action.Controller, Action.Method, context, PassParams.ToArray());
                }
                else
                {
                    ErrorMsg = "请求路径不存在！";
                }
            }
            else if (context.Request.HttpMethod == "POST")
            {
                RoutePath = context.Request.RawUrl.Substring(1);

                if (VampirewalCoreWebApiExtension.Routes.TryGetValue(RoutePath, out var Action))
                {
                    // 读取请求中的流并转成string
                    using var reader = new StreamReader(context.Request.InputStream);
                    var requestBody = await reader.ReadToEndAsync();

                    // POST请求只获取第一个参数的类型
                    var parameterType = Action.Method.GetParameters()[0].ParameterType;
                    //根据类型反序列化成object
                    var requestObj = JsonSerializer.Deserialize(requestBody, parameterType);

                    await ExcuteAction(Action.Controller, Action.Method, context, requestObj);
                }
                else
                {
                    ErrorMsg = "请求路径不存在！";
                }
            }
            else
            {
                ErrorMsg = "仅支持GET和POST请求！";
            }

            if (!string.IsNullOrEmpty(ErrorMsg))
            {
                Mate.Code = 404;
                Mate.Msg = ErrorMsg;
                goto End;
            }
            else
            {
                return;
            }
        }
        catch (Exception ex)
        {
            Mate.Code = 500;
            Mate.Msg = "程序异常";
            Mate.Ex = ex;
            goto End;
        }

        End:
        if (Mate.Code != 200)
        {
            context.Response.StatusCode = Mate.Code;
            if (Mate.Ex != null)
            {
                await _ApiResult.OnErrorResponse(context, new VampirewalWebApiException(Mate.Msg, Mate.Code, Mate.Ex));
            }
            else
            {
                await _ApiResult.OnErrorResponse(context, new VampirewalWebApiException(Mate.Msg, Mate.Code));
            }
        }
    }

    private object StringToOtherConver(Type targetType, string OldValue)
    {
        if (targetType == typeof(string))
            return OldValue;
        if (targetType == typeof(Int32))
            return Convert.ToInt32(OldValue);
        if (targetType == typeof(Int64))
            return Convert.ToInt64(OldValue);
        if (targetType == typeof(Double))
            return Convert.ToDouble(OldValue);
        if (targetType == typeof(Decimal))
            return Convert.ToDecimal(OldValue);
        if (targetType == typeof(DateTime))
            return Convert.ToDateTime(OldValue);

        return null;
    }

    /// <summary>
    /// 执行方法
    /// </summary>
    /// <param name="controllerType"></param>
    /// <param name="method"></param>
    /// <param name="context"></param>
    /// <param name="param"></param>
    /// <returns></returns>
    private async Task ExcuteAction(Type controllerType, MethodInfo method, HttpListenerContext context, params object[] param)
    {
        var ActionFilters = method.GetCustomAttributes().Where(w => w.GetType().GetInterface("IVampirewalActionFilter") != null).Select(s => (IVampirewalActionFilter)s).ToList();

        VampirewalActionFilterPipeline pipeline = new VampirewalActionFilterPipeline(ActionFilters);

        VampirewalActionFilterContext filter = new VampirewalActionFilterContext(context, controllerType, method, param);

        var result = await pipeline.InvokeAsync(filter);

        if (result)
        {
            //从DI容器中获取到Controller
            object controllerInstance = _serviceProvider.GetRequiredService(controllerType);

            try
            {
                // 执行方法并获取返回值
                var actionResult = method.Invoke(controllerInstance, param);

                await _ApiResult.OnSuccessResponse(context, actionResult);
            }
            catch (Exception ex) 
            {
                if (ex.InnerException is VampirewalWebApiException exc)
                {
                    await _ApiResult.OnErrorResponse(context, exc);
                }
                else
                {
                    await _ApiResult.OnErrorResponse(context, new VampirewalWebApiException(ex.Message, 404));
                }
            }

        }
        else
        {
            context.Response.StatusCode = 404;
            await _ApiResult.OnErrorResponse(context, new VampirewalWebApiException(filter.ErrorInfo.ToString(), 404));
        }
    }
}